%%--- coding:utf-8 ---
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% File Name: mc_pool_api
%%% Created on : 2024/9/3 8:34
%%% @author Gaylen 252323463@qq.com
%%% @copyright (C) 2024, freedom
%%% @doc
%%%
%%% @end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-module(mc_pool_api).
-author("Gaylen").
-include("mongodb_driver.hrl").

%% API
-export([
    start_pool/2,
    stop_pool/1,

    command/3,
    command/4,

    insert/4,

    update/5,
    update/7,

    delete_all/4,
    delete_one/4,

    find_one/4,
    find_one/5,

    find/4,
    find/5,
    find/6,
    find/8,
    find/9,

    find_and_modify/5,
    find_and_modify/7,

    count/4,
    count/6,

    ensure_index/5,
    ensure_index/6
]).

%% Options
%%   host 数据库ip,默认"127.0.0.1"
%%   port 数据库端口,默认27017
%%   host_res 数据库副本集地址, [{"数据库ip", 端口}, ...]
%%   database 访问的数据库名,默认<<"test">>
%%   user 用户名
%%   password 密码
%%   auth_source 授权源，一般为 <<"admin">>
%%   ssl  是否使用ssl连接, true|false
%%   ssl_opts SSL参数
%%   timeout 连接超时,单位毫秒,默认infinity
%%   register 注册进程别名
%%   worker_size 连接进程的数量，默认10条连接
%%   min_pool_size   读取解释doc的工作进程的最小数量，默认10
%%   max_pool_size   读取解释doc的工作进程的最大数量，默认unlimited 不限制
start_pool(ManageRegName, Options) ->
    mc_manager:start_link(ManageRegName, Options).

stop_pool(ManageRegName) ->
    mc_manager:close(ManageRegName).

-spec command(atom(), database(), selector()) -> {boolean(), map()}.
command(ManageRegName, Database, Command) ->
    command(ManageRegName, Database, Command, false).

-spec command(atom(), database(), selector(), boolean()) -> {boolean(), map()}.
command(ManageRegName, Database, Command, IsSlaveOk) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_command/5, [Database, Command, IsSlaveOk]).

-spec insert(atom(), database(), collection(), [bson:document()] | bson:document()) -> {boolean(), map()}.
insert(ManageRegName, Database, Table, Docs) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_insert/5, [Database, Table, Docs]).

-spec update(atom(), database(), collection(), selector(), map() | bson:document()) -> {boolean(), map()}.
update(ManageRegName, Database, Table, Selector, Doc) ->
    update(ManageRegName, Database, Table, Selector, Doc, false, false).

-spec update(atom(), database(), collection(), selector(), map() | bson:document(), boolean(), boolean()) -> {boolean(), map()}.
update(ManageRegName, Database, Table, Selector, Doc, IsUpInsert, IsMultiUpdate) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_update/8, [Database, Table, Selector, Doc, IsUpInsert, IsMultiUpdate]).

-spec delete_all(atom(), database(), collection(), selector()) -> {boolean(), map()}.
delete_all(ManageRegName, Database, Table, Selector) ->
    delete_limit(ManageRegName, Database, Table, Selector, 0).
-spec delete_one(atom(), database(), collection(), selector()) -> {boolean(), map()}.
delete_one(ManageRegName, Database, Table, Selector) ->
    delete_limit(ManageRegName, Database, Table, Selector, 1).
%% @private
delete_limit(ManageRegName, Database, Table, Selector, Limit) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_delete/6, [Database, Table, Selector, Limit]).

-spec find_one(atom(), database(), collection(), selector()) -> {boolean(), undefined | map()}.
find_one(ManageRegName, Database, Table, Selector) ->
    find_one(ManageRegName, Database, Table, Selector, #{}).
-spec find_one(atom(), database(), collection(), selector(), map()) -> {boolean(), undefined | map()}.
find_one(ManageRegName, Database, Table, Selector, Projector) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_find_one/6, [Database, Table, Selector, Projector]).

-spec find(atom(), database(), collection(), selector()) -> {true, [map()], cursor() | undefined} | {false, map()}.
find(ManageRegName, Database, Table, Selector) ->
    find(ManageRegName, Database, Table, Selector, #{}).
-spec find(atom(), database(), collection(), selector(), map()) -> {true, [map()], cursor() | undefined} | {false, map()}.
find(ManageRegName, Database, Table, Selector, SortDoc) ->
    find(ManageRegName, Database, Table, Selector, SortDoc, #{}).
-spec find(atom(), database(), collection(), selector(), map(), map()) -> {true, [map()], cursor() | undefined} | {false, map()}.
find(ManageRegName, Database, Table, Selector, SortDoc, Projector) ->
    find(ManageRegName, Database, Table, Selector, SortDoc, Projector, 0, 0).
-spec find(atom(), database(), collection(), selector(), map(), map(), integer(), integer()) -> {true, [map()], cursor() | undefined} | {false, map()}.
find(ManageRegName, Database, Table, Selector, SortDoc, Projector, Skip, Limit) ->
    find(ManageRegName, Database, Table, Selector, SortDoc, Projector, Skip, Limit, false).
-spec find(atom(), database(), collection(), selector(), map(), map(), integer(), integer(), boolean()) -> {true, [map()], cursor() | undefined} | {false, map()}.
find(ManageRegName, Database, Table, Selector, SortDoc, Projector, Skip, Limit, IsSlave) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_find/10, [Database, Table, Selector, SortDoc, Projector, Skip, Limit, IsSlave]).

%%%%%%%%%% find_and_modify %%%%%%%%%%%%%%%%%%%%%%%%
-spec find_and_modify(atom(), database(), collection(), selector(), map() | bson:document()) -> {boolean(), map()}.
find_and_modify(ManageRegName, Database, Table, Selector, UpdateDoc) ->
    find_and_modify(ManageRegName, Database, Table, Selector, UpdateDoc, #{}, true).
-spec find_and_modify(atom(), database(), collection(), selector(), map() | bson:document(), map() | bson:document(), boolean()) -> {boolean(), map()}.
find_and_modify(ManageRegName, Database, Table, Selector, UpdateDoc, Projector, IsUpInsert) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_find_and_modify/8, [Database, Table, Selector, UpdateDoc, Projector, IsUpInsert]).

-spec count(atom(), database(), collection(), selector()) -> {boolean(), integer()}.
count(ManageRegName, Database, Table, Selector) ->
    count(ManageRegName, Database, Table, Selector, 0, 0).
-spec count(atom(), database(), collection(), selector(), integer(), integer()) -> {boolean(), integer()}.
count(ManageRegName, Database, Table, Selector, Limit, Skip) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_count/7, [Database, Table, Selector, Limit, Skip]).

-spec ensure_index(atom(), database(), collection(), bson:document(), boolean()) -> {boolean(), map()}.
ensure_index(ManageRegName, Database, Table, IndexSpecs, Unique) ->
    ensure_index(ManageRegName, Database, Table, IndexSpecs, Unique, false).

-spec ensure_index(atom(), database(), collection(), bson:document(), boolean(), boolean()) -> {boolean(), map()}.
ensure_index(ManageRegName, Database, Table, IndexSpecs, Unique, DropDups) ->
    priv_mongo_req(ManageRegName, fun mc_pool_logic:sync_ensure_index/7, [Database, Table, IndexSpecs, Unique, DropDups]).

%%private
priv_mongo_req(ManageRegName, Func, Args) ->
    case mc_manager:sync_apply(ManageRegName, fun mc_manager_logic:sync_fetch_worker_and_pool/1, []) of
        {ok, WorkerPid, PoolPid} ->
            Result = mc_pool:sync_apply(PoolPid, Func, [WorkerPid | Args]),
            mc_manager:async_apply(ManageRegName, fun mc_manager_logic:async_recycle_pool/2, [PoolPid]),
            Result;
        {error, Reason} ->
            {false, #{<<"reason">> => Reason}}
    end.

